package com.lagou.edu.factory;

import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.utils.TransactionManager;
import com.mysql.jdbc.StringUtils;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.io.File;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public class AnnotationBeanFactory {

    //bean实例池
    private static Map<String, Object> map = new HashMap<>();

    static{
        //扫描service注解类并实例化

//        String scanPackage = "com.lagou.edu.service.impl";
        String scanPackage = "com.lagou.edu";
        //注入属性
        instantiateServiceAnotationBean(scanPackage);

        //遍历bean工厂，注入依赖属性
        injectProperties();


        //检查@Transactional注解 动态代理增强对象
        for(String beanDefiantionName : map.keySet()){
           Object bean = map.get(beanDefiantionName);
           Method[] declaredMethods = bean.getClass().getDeclaredMethods();
           for(Method method : declaredMethods){
               Annotation[] annotations = method.getAnnotations();
               String annotationMethodName = method.getName();
               for(Annotation anno : annotations){
                   if(anno.annotationType() == Transactional.class){
                       Object proxyInstance = null;
                       if(bean.getClass().getInterfaces() == null || bean.getClass().getInterfaces().length == 0){
                           //没有实现的接口走cglib
                           proxyInstance = getCGLIBProxyInstance(bean, annotationMethodName);
                       }else{
                           proxyInstance = getJDKProxyInstance(bean, annotationMethodName);
                       }

                       map.put(beanDefiantionName, proxyInstance);
                       String s = "";
                   }
               }
           }
       }

    }

    /**
     * CGLIB动态代理增加事务功能
     * @param obj
     * @param annotationMethodName
     * @return
     */
    private static Object getCGLIBProxyInstance(Object obj, String annotationMethodName) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(obj.getClass());
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                Object result = null;
                if (annotationMethodName.equals(method.getName())) {
                    TransactionManager transactionManager = (TransactionManager) map.get("transactionManager");
                    transactionManager.beginTransaction();
                    System.out.println("in cglibproxy bengintrasction con=" + transactionManager.getConnectionUtils().getCurrentThreadConn());
                    try {
                        result = method.invoke(obj, args);
//                        result = methodProxy.invokeSuper(o, args);
                        transactionManager.commit();
                        System.out.println("in cglibproxy commit con=" + transactionManager.getConnectionUtils().getCurrentThreadConn());
                    } catch (Exception e) {
                        e.printStackTrace();
                        transactionManager.rollback();
                        System.out.println("in cglibproxy rollback con=" + transactionManager.getConnectionUtils().getCurrentThreadConn());
                        throw e;
                    }
                } else {
                    result = method.invoke(obj, args);
                }

                return result;
            }
        });

        return enhancer.create();

    }

    /**
     * jdk动态代理增加事务功能
     * @param obj
     * @param annotationMethodName
     * @return
     */
    private static Object getJDKProxyInstance(Object obj, String annotationMethodName) {
        return Proxy.newProxyInstance(AnnotationBeanFactory.class.getClassLoader(), obj.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Object result = null;
                if (annotationMethodName.equals(method.getName())) {
                    TransactionManager transactionManager = (TransactionManager) map.get("transactionManager");
                    transactionManager.beginTransaction();
                    System.out.println("in jdkproxy bengintrasction con=" + transactionManager.getConnectionUtils().getCurrentThreadConn());
                    try {
                        result = method.invoke(obj, args);
                        transactionManager.commit();
                        System.out.println("in jdkproxy commit con=" + transactionManager.getConnectionUtils().getCurrentThreadConn());
                    } catch (Exception e) {
                        e.printStackTrace();
                        transactionManager.rollback();
                        System.out.println("in jdkproxy rollback con=" + transactionManager.getConnectionUtils().getCurrentThreadConn());
                        throw e;
                    }
                } else {
                    result = method.invoke(obj, args);
                }

                return result;
            }
        });
    }

    /**
     * 实例化service注解修饰的bean 放入beanfactory
     * @param scanPackage
     */
    private static void instantiateServiceAnotationBean(String scanPackage) {
        try {
            ClassLoader classLoader = AnnotationBeanFactory.class.getClassLoader();
            String scanPackageInFileSystem = scanPackage.replace(".", "/");
            URL resource = classLoader.getResource(scanPackageInFileSystem + "/");
            File file = new File(resource.toURI());
            File[] files = file.listFiles();
            if(files != null){
                for(File subFile : files){
                    String str = subFile.getAbsolutePath();
                    if(subFile.isDirectory()){
                        instantiateServiceAnotationBean(scanPackage + "." + subFile.getName());
                    }else{
                        String fileName = subFile.getName();
                        if(fileName.endsWith(".class")){
                            String className = fileName.substring(0, fileName.lastIndexOf("."));
                            String fullQualifiedClassName = scanPackage + "." + className;
                            Class<?> aClass = Class.forName(fullQualifiedClassName);
                            //实例化bean
                            Object classBeanObject = null;
                            if(!map.containsKey(fullQualifiedClassName)){
                                Annotation[] annotations = aClass.getAnnotations();
                                for(Annotation annotation : annotations){
                                    if (annotation.annotationType() == (Service.class)){
                                        Service serviceAnotation = (Service) annotation;
                                        //获取service的value值
                                        String beanDefinationName = getBeanDefinationName(serviceAnotation, aClass);

                                        classBeanObject = aClass.newInstance();
                                        //实例化到bean工厂
                                        map.put(beanDefinationName, classBeanObject);
                                        System.out.println("map add " + beanDefinationName);
                                    }
                                }
                            }else{
                                classBeanObject = map.get(fullQualifiedClassName);
                            }
                        }

                    }

                }
            }
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    private static String getBeanDefinationName(Service serviceAnotation, Class<?> beanClass) {
        String beanName = serviceAnotation.value();
        String definationName = "";
        if(beanName== null || "".equals(beanName)){
            definationName = beanClass.getSimpleName().substring(0,1).toLowerCase() + beanClass.getSimpleName().substring(1);
        }else{
            definationName = beanName;
        }
        return definationName;
    }


    /**
     * 遍历对bean工厂中的实例，注入实例的Autowired修饰的属性对象
     */
    private static void injectProperties() {
        try {
            for(String name : map.keySet()){
                Object classBeanObject = map.get(name);
                Field[] declaredFields = classBeanObject.getClass().getDeclaredFields();
                for(Field field : declaredFields){
                    Annotation[] annotations = field.getAnnotations();
                    for(Annotation ano : annotations){
                        if(ano.annotationType().equals(Autowired.class)){
                            String beanName = field.getName();
                            Object fieldBeanObject = map.get(beanName);
                            if(fieldBeanObject != null){
                                field.setAccessible(true);
                                field.set(classBeanObject, fieldBeanObject);
                            }

                        }
                    }
                }
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }


    //实例化bean
    private static void initBeans(){

    }

    //获取bean接口
    public static Object getBean(String beanName){
        return map.get(beanName);
    }

//    if(fieldBeanObject == null){
        //beanfacotry中不存在，先实例化此对象
//                                Class<?> fileClass = Class.forName(type.getTypeName());
//                                fieldBeanObject = fileClass.newInstance();
//
//                                if(fileClass.isInterface()){
//                                    System.out.println("注入属性失败，没有匹配的");
//                                }


//                                if(fileClass.isInterface()){
//                                    //如果属性是接口类型
//                                    for(Object o : map.values()){
//                                        if(o.getClass().isAssignableFrom(fileClass)){
//                                            fieldBeanObject = o;
//                                        }
//                                    }
//                                }else{
//
//                                }
//    }


}
